import { Point } from "/@/components/types";

//两点间距离
export function calc2pointDistance(p1: Point, p2: Point) {
  const dx = p2.x - p1.x;
  const dy = p2.y - p1.y;
  return Math.sqrt(dx * dx + dy * dy);
}

/**
 * po是直线p1p2上的一点，直线k0垂直于p1p2并穿过p0，求直线k0上与p0距离为distance的点
 * @param p1 直线上的一点
 * @param p2 直线上的另一点
 * @param p0 目标线与已知线段的垂直交点
 * @param distance 目标点与垂直交点的直线距离
 * @param isAboveP1P2 根据上面描述，最终求出的点有两个，该标志决定取哪个；isAboveP1P2为true, 取相对P1P2方向左边的点，反之右边的点；
 * @returns 目标点
 */
export function getVerticalDitancePointByPoint(
  p1: Point,
  p2: Point,
  p0: Point,
  distance: number,
  isAboveP1P2 = true
): Point {
  if (p1.x === p2.x) {
    return {
      x: isAboveP1P2 ? p1.x + distance : p1.x - distance,
      y: p0.y,
    };
  }
  if (p1.y === p2.y) {
    return {
      x: p0.x,
      y: isAboveP1P2 ? p1.y - distance : p1.y + distance,
    };
  }
  const k12 = (p2.y - p1.y) / (p2.x - p1.x);
  const k0 = -1 / k12;
  /**
   * 设目标点为px，则有
   * (px.y - p0.y) / (px.x - p0.x) = k0
   * (px.y - p0.y)² + (px.x - p0.x)² = distance²
   * 所以
   * (px.x - p0.x)² = distance² - (k0 * (px.x - p0.x))²
   * (px.x - p0.x)² = distance² / (1 + k0²)
   * 设dx0 = px.x - p0.x
   */
  const dx0 = Math.sqrt(Math.pow(distance, 2) / (1 + Math.pow(k0, 2)));
  const px = { x: 0, y: 0 };
  const b0 = p0.y - k0 * p0.x;
  if (p2.y > p1.y) {
    if (isAboveP1P2) {
      px.x = p0.x + dx0;
    } else {
      px.x = p0.x - dx0;
    }
  } else {
    if (isAboveP1P2) {
      px.x = p0.x - dx0;
    } else {
      px.x = p0.x + dx0;
    }
  }
  px.y = px.x * k0 + b0;
  return px;
}

/**
 * 根据矩形区域两条边的中点，算出矩形区域的4个顶点
 * @param p1 其中一条边的中点
 * @param p2 另一条边的中点
 * @param sideLen 相对p1p2另一条边的长度
 */
export function getRectPointsBy2SideCenterPoint(p1: Point, p2: Point, sideLen: number) {
  const pr1 = getVerticalDitancePointByPoint(p1, p2, p1, sideLen / 2, true);
  const pr2 = getVerticalDitancePointByPoint(p1, p2, p1, sideLen / 2, false);
  const pr3 = getVerticalDitancePointByPoint(p1, p2, p2, sideLen / 2, true);
  const pr4 = getVerticalDitancePointByPoint(p1, p2, p2, sideLen / 2, false);
  return {
    p1: pr1,
    p2: pr2,
    p3: pr3,
    p4: pr4,
  };
}

/**
 * 目标点是否在4个点组成的矩形区域中
 * 矩形区域4条边分别是p1p2 p1p3 p3p4 p2p4,其中p1p2平行于p3p4,p1p3平行于p2p4
 * @param p 目标点
 */
export function isPointInRect(p: Point, p1: Point, p2: Point, p3: Point /*, p4: Point*/): boolean {
  let maxX, minX, maxY, minY;
  if (p2.x === p1.x) {
    maxX = Math.max(p1.x, p3.x);
    minX = Math.min(p1.x, p3.x);
    maxY = Math.max(p1.y, p2.y);
    minY = Math.min(p1.y, p2.y);
  } else if (p1.x === p3.x) {
    maxX = Math.max(p1.x, p2.x);
    minX = Math.min(p1.x, p2.x);
    maxY = Math.max(p1.y, p3.y);
    minY = Math.min(p1.y, p3.y);
  }
  if (maxX !== undefined) {
    return p.x >= minX && p.x <= maxX && p.y >= minY && p.y <= maxY;
  }
  const k12 = (p2.y - p1.y) / (p2.x - p1.x);
  const k13 = (p3.y - p1.y) / (p3.x - p1.x);
  const b12 = p1.y - k12 * p1.x;
  const b34 = p3.y - k12 * p3.x;
  const b13 = p1.y - k13 * p1.x;
  const b24 = p2.y - k13 * p2.x;
  const y12 = k12 * p.x + b12;
  const y34 = k12 * p.x + b34;
  const y13 = k13 * p.x + b13;
  const y24 = k13 * p.x + b24;
  return (
    ((p.y >= y12 && p.y <= y34) || (p.y >= y34 && p.y <= y12)) &&
    ((p.y >= y13 && p.y <= y24) || (p.y >= y24 && p.y <= y13))
  );
}

/**
 * 两个跟x轴y轴平行的矩形是否相交
 * @param p1 矩形1左上角的点
 * @param w1 矩形1宽度
 * @param h1 矩形1高度
 * @param p2 矩形2左上角的点
 * @param w2 矩形2宽度
 * @param h2 矩形2高度
 */
export function isRectIntersect(p1: Point, w1: number, h1: number, p2: Point, w2: number, h2: number) {
  const aCenterX = p1.x + w1 / 2;
  const aCenterY = p1.y + h1 / 2;
  const bCenterX = p2.x + w2 / 2;
  const bCenterY = p2.y + h2 / 2;
  const radiusX = w1 / 2 + w2 / 2;
  const radiusY = h1 / 2 + h2 / 2;
  //判断两个矩形的中心坐标的水平和垂直距离
  return Math.abs(aCenterX - bCenterX) < radiusX && Math.abs(aCenterY - bCenterY) < radiusY;
}

/**
 * https://www.cnblogs.com/jiahuafu/p/4024661.html
 * 判断线段是否在矩形内
 * 先看线段所在直线是否与矩形相交，
 * 如果不相交则返回false，
 * 如果相交，
 * 则看线段的两个点是否在矩形的同一边（即两点的x(y)坐标都比矩形的小x(y)坐标小，或者大）,
 * 若在同一边则返回false，
 * 否则就是相交的情况。
 * @param linePointX1 线段起始点x坐标
 * @param linePointY1 线段起始点y坐标
 * @param linePointX2 线段结束点x坐标
 * @param linePointY2 线段结束点y坐标
 * @param rectangleLeftTopX 矩形左上点x坐标
 * @param rectangleLeftTopY 矩形左上点y坐标
 * @param rectangleRightBottomX 矩形右下点x坐标
 * @param rectangleRightBottomY 矩形右下点y坐标
 * @return 是否相交
 */
export function isLineIntersectRectangle(
  linePointX1: number,
  linePointY1: number,
  linePointX2: number,
  linePointY2: number,
  rectangleLeftTopX: number,
  rectangleLeftTopY: number,
  rectangleRightBottomX: number,
  rectangleRightBottomY: number
): boolean {
  const lineHeight: number = linePointY1 - linePointY2;
  const lineWidth: number = linePointX2 - linePointX1; // 计算叉乘
  const c: number = linePointX1 * linePointY2 - linePointX2 * linePointY1;
  if (
    (lineHeight * rectangleLeftTopX + lineWidth * rectangleLeftTopY + c >= 0 &&
      lineHeight * rectangleRightBottomX + lineWidth * rectangleRightBottomY + c <= 0) ||
    (lineHeight * rectangleLeftTopX + lineWidth * rectangleLeftTopY + c <= 0 &&
      lineHeight * rectangleRightBottomX + lineWidth * rectangleRightBottomY + c >= 0) ||
    (lineHeight * rectangleLeftTopX + lineWidth * rectangleRightBottomY + c >= 0 &&
      lineHeight * rectangleRightBottomX + lineWidth * rectangleLeftTopY + c <= 0) ||
    (lineHeight * rectangleLeftTopX + lineWidth * rectangleRightBottomY + c <= 0 &&
      lineHeight * rectangleRightBottomX + lineWidth * rectangleLeftTopY + c >= 0)
  ) {
    if (rectangleLeftTopX > rectangleRightBottomX) {
      const temp: number = rectangleLeftTopX;
      rectangleLeftTopX = rectangleRightBottomX;
      rectangleRightBottomX = temp;
    }
    if (rectangleLeftTopY < rectangleRightBottomY) {
      const temp1: number = rectangleLeftTopY;
      rectangleLeftTopY = rectangleRightBottomY;
      rectangleRightBottomY = temp1;
    }
    if (
      (linePointX1 < rectangleLeftTopX && linePointX2 < rectangleLeftTopX) ||
      (linePointX1 > rectangleRightBottomX && linePointX2 > rectangleRightBottomX) ||
      (linePointY1 > rectangleLeftTopY && linePointY2 > rectangleLeftTopY) ||
      (linePointY1 < rectangleRightBottomY && linePointY2 < rectangleRightBottomY)
    ) {
      return false;
    } else {
      return true;
    }
  } else {
    return false;
  }
}

function getPositiveDeg(deg1, deg2) {
  const d = deg1 - deg2;
  return d > 0 ? d : 360 + d;
}

/**
 * 求出线段p1p旋转到线段p2p所需的角度值
 * @returns 旋转角度值
 */
export function getPointDeg(p: Point, p1: Point, p2: Point) {
  const d1 = calc2pointDistance(p1, p);
  const d2 = calc2pointDistance(p2, p);
  let deg1 = Math.acos(Math.abs(p1.x - p.x) / d1);
  deg1 = (180 / Math.PI) * deg1;
  let deg2 = Math.acos(Math.abs(p2.x - p.x) / d2);
  deg2 = (180 / Math.PI) * deg2;
  const isUp = p1.y <= p.y;
  if (p1.x >= p.x) {
    if (p2.x >= p.x && p2.y <= p.y) {
      return isUp ? getPositiveDeg(deg1, deg2) : 360 - deg1 - deg2;
    }
    if (p2.x >= p.x && p2.y >= p.y) {
      return isUp ? deg1 + deg2 : getPositiveDeg(deg2, deg1);
    }
    if (p2.x <= p.x && p2.y >= p.y) {
      return isUp ? deg1 + 90 + 90 - deg2 : 90 - deg1 + 90 - deg2;
    }
    // if(p2.x <= p.x && p2.y <= p.y){

    // }
    return isUp ? deg1 + 180 + deg2 : 90 - deg1 + 90 + deg2;
  } else {
    if (p2.x >= p.x && p2.y <= p.y) {
      return isUp ? 90 - deg1 + 90 - deg2 : deg1 + 90 + 90 - deg2;
    }
    if (p2.x >= p.x && p2.y >= p.y) {
      return isUp ? 90 - deg1 + 90 + deg2 : deg1 + 180 + deg2;
    }
    if (p2.x <= p.x && p2.y >= p.y) {
      return isUp ? 360 - deg1 - deg2 : getPositiveDeg(deg1, deg2);
    }
    // if(p2.x <= p.x && p2.y <= p.y){

    // }
    return isUp ? getPositiveDeg(deg2, deg1) : deg1 + deg2;
  }
}

//角度转为弧度
function degToRadian(deg) {
  return deg / (180 / Math.PI);
}

function radianToDeg(radian) {
  return radian * (180 / Math.PI);
}

/**
 * 求出p1相对于p顺时针旋转deg后的坐标
 * TODO: 代码冗余 需要优化
 */
export function getRotatePoint(p: Point, p1: Point, deg: number): Point {
  if (!deg || deg % 360 === 0) return p1;
  const d = calc2pointDistance(p, p1);
  //求出p1与x轴夹角
  let deg1 = Math.acos(Math.abs(p1.x - p.x) / d);
  deg1 = (180 / Math.PI) * deg1;
  if (p1.x >= p.x) {
    if (p1.y <= p.y) {
      if (deg <= deg1) {
        let deg2 = deg1 - deg;
        deg2 = degToRadian(deg2);
        const dx = Math.cos(deg2) * d;
        const dy = Math.sin(deg2) * d;
        return { x: p.x + dx, y: p.y - dy };
      } else {
        let ddeg = deg - deg1;
        if (ddeg <= 90) {
          ddeg = degToRadian(ddeg);
          const dx = Math.cos(ddeg) * d;
          const dy = Math.sin(ddeg) * d;
          return { x: p.x + dx, y: p.y + dy };
        } else if (ddeg <= 180) {
          ddeg = 180 - ddeg;
          ddeg = degToRadian(ddeg);
          const dx = Math.cos(ddeg) * d;
          const dy = Math.sin(ddeg) * d;
          return { x: p.x - dx, y: p.y + dy };
        } else if (ddeg <= 270) {
          ddeg = ddeg - 180;
          ddeg = degToRadian(ddeg);
          const dx = Math.cos(ddeg) * d;
          const dy = Math.sin(ddeg) * d;
          return { x: p.x - dx, y: p.y - dy };
        } else {
          ddeg = 360 - ddeg;
          ddeg = degToRadian(ddeg);
          const dx = Math.cos(ddeg) * d;
          const dy = Math.sin(ddeg) * d;
          return { x: p.x + dx, y: p.y - dy };
        }
      }
    } else {
      let ddeg = 90 - deg1;
      if (deg <= ddeg) {
        let deg2 = deg + deg1;
        deg2 = degToRadian(deg2);
        const dx = Math.cos(deg2) * d;
        const dy = Math.sin(deg2) * d;
        return { x: p.x + dx, y: p.y + dy };
      } else {
        ddeg = deg - ddeg;
        if (ddeg <= 90) {
          ddeg = 90 - ddeg;
          ddeg = degToRadian(ddeg);
          const dx = Math.cos(ddeg) * d;
          const dy = Math.sin(ddeg) * d;
          return { x: p.x - dx, y: p.y + dy };
        } else if (ddeg <= 180) {
          ddeg = ddeg - 90;
          ddeg = degToRadian(ddeg);
          const dx = Math.cos(ddeg) * d;
          const dy = Math.sin(ddeg) * d;
          return { x: p.x - dx, y: p.y - dy };
        } else if (ddeg <= 270) {
          ddeg = 270 - ddeg;
          ddeg = degToRadian(ddeg);
          const dx = Math.cos(ddeg) * d;
          const dy = Math.sin(ddeg) * d;
          return { x: p.x + dx, y: p.y - dy };
        } else {
          ddeg = ddeg - 270;
          ddeg = degToRadian(ddeg);
          const dx = Math.cos(ddeg) * d;
          const dy = Math.sin(ddeg) * d;
          return { x: p.x + dx, y: p.y + dy };
        }
      }
    }
  } else {
    if (p1.y <= p.y) {
      let ddeg = 90 - deg1;
      if (deg <= ddeg) {
        let deg2 = deg + deg1;
        deg2 = degToRadian(deg2);
        const dx = Math.cos(deg2) * d;
        const dy = Math.sin(deg2) * d;
        return { x: p.x - dx, y: p.y - dy };
      } else {
        ddeg = deg - ddeg;
        if (ddeg <= 90) {
          ddeg = 90 - ddeg;
          ddeg = degToRadian(ddeg);
          const dx = Math.cos(ddeg) * d;
          const dy = Math.sin(ddeg) * d;
          return { x: p.x + dx, y: p.y - dy };
        } else if (ddeg <= 180) {
          ddeg = ddeg - 90;
          ddeg = degToRadian(ddeg);
          const dx = Math.cos(ddeg) * d;
          const dy = Math.sin(ddeg) * d;
          return { x: p.x + dx, y: p.y + dy };
        } else if (ddeg <= 270) {
          ddeg = 270 - ddeg;
          ddeg = degToRadian(ddeg);
          const dx = Math.cos(ddeg) * d;
          const dy = Math.sin(ddeg) * d;
          return { x: p.x - dx, y: p.y + dy };
        } else {
          ddeg = ddeg - 270;
          ddeg = degToRadian(ddeg);
          const dx = Math.cos(ddeg) * d;
          const dy = Math.sin(ddeg) * d;
          return { x: p.x - dx, y: p.y - dy };
        }
      }
    } else {
      if (deg <= deg1) {
        let deg2 = deg1 - deg;
        deg2 = degToRadian(deg2);
        const dx = Math.cos(deg2) * d;
        const dy = Math.sin(deg2) * d;
        return { x: p.x - dx, y: p.y + dy };
      } else {
        let ddeg = deg - deg1;
        if (ddeg <= 90) {
          ddeg = degToRadian(ddeg);
          const dx = Math.cos(ddeg) * d;
          const dy = Math.sin(ddeg) * d;
          return { x: p.x - dx, y: p.y - dy };
        } else if (ddeg <= 180) {
          ddeg = 180 - ddeg;
          ddeg = degToRadian(ddeg);
          const dx = Math.cos(ddeg) * d;
          const dy = Math.sin(ddeg) * d;
          return { x: p.x + dx, y: p.y - dy };
        } else if (ddeg <= 270) {
          ddeg = ddeg - 180;
          ddeg = degToRadian(ddeg);
          const dx = Math.cos(ddeg) * d;
          const dy = Math.sin(ddeg) * d;
          return { x: p.x + dx, y: p.y + dy };
        } else {
          ddeg = 360 - ddeg;
          ddeg = degToRadian(ddeg);
          const dx = Math.cos(ddeg) * d;
          const dy = Math.sin(ddeg) * d;
          return { x: p.x - dx, y: p.y + dy };
        }
      }
    }
  }
}

/**
 * 已知一条直线k的方程、直线上的一点p1和直线外一个点p2，求出k与垂直于k并穿过p2的直线的交点
 * @param p1 已知直线上的一点，
 * @param line1 已知直线方程值，约定为null的时候，直线垂直于x轴
 * @param p2 直线外的一点
 * @returns 交点
 */
export function getIntersectionByLineAndPoint(p1: Point, line1: { k: number; b: number } | null, p2: Point): Point {
  if (line1 === null) {
    return {
      x: p1.x,
      y: p2.y,
    };
  } else if (line1.k === 0) {
    return {
      x: p2.x,
      y: p1.y,
    };
  } else {
    const k2 = -1 / line1.k;
    const b2 = p2.y - k2 * p2.x;
    const targetX = (b2 - line1.b) / (line1.k - k2);
    const targetY = k2 * targetX + b2;
    return {
      x: targetX,
      y: targetY,
    };
  }
}

//线段与x轴夹角
export function getAngelByP1P2AndXaxis(p1: Point, p2: Point) {
  const d = calc2pointDistance(p1, p2);
  if (!d) return 0;
  const cos = (p2.x - p1.x) / d;
  let radian = Math.acos(cos);
  if (p2.y < p1.y) {
    radian = Math.PI * 2 - radian;
  }
  return radian * (180 / Math.PI);
}

/**
 * 求出线段与x轴夹角
 * @param p1 线段起始点
 * @param p2 线段终点
 */
export function getDegToXaxis(p1: Point, p2: Point) {
  const d = calc2pointDistance(p1, p2);
  const dx = Math.abs(p1.x - p2.x);
  const cos = dx / d;
  const radian = Math.acos(cos);
  return radianToDeg(radian);
}
